#ifndef _MDYNAMICGRBGMANAGER_HPP_
#define _MDYNAMICGRBGMANAGER_HPP_

#include "MMacros.h"
#include "MDataManager.hpp"
#include "MVideoManager.hpp"
using namespace cv;
using namespace std;

class MDynamicGrBgManager : public Mat {
/*	<description>
		dynamic background model
		FIFO: queue every frame online
		compute average of queueing images
		queued images are 8-bit gray-scale
		optimal window-size is 200
	</description>
	<usage>
		init image queue at start point
		refresh queue online during tracing
		compute average on call (gray-scale)
	</usage>
*/
private:
	MVideoManager* _pClips; // pointer to manager of video clips
	int _iFrStart; // start frame to initialize background
	Mat _gr, _gr32, _sum32, _avg32; // cpu single/summed/averaged frame buffer
	deque<Mat> _QFrGr32; // buffer queue
	int _cQSize; // queue size

public:
	//constructors
	MDynamicGrBgManager() {
		_cQSize = mod_dynamic_length; // macro: default window size of queueing images
	}

	// clear
	void clear() { // clearing buffer of the queue
		_QFrGr32.clear();
	}

	//set Queue size
	void setQSize(int cQSize) {
		_cQSize = cQSize; // change the window size 
	}
	
	// init background
	bool initWith(MVideoManager* pReader) {
		//check if video has been bound
		if(!pReader) return false;
		_pClips = pReader;
		
		//get video info
		_iFrStart = pReader->getLength();
		int wFr = (int)pReader->getClipPtr()->get(CV_CAP_PROP_FRAME_WIDTH);
		int hFr = (int)pReader->getClipPtr()->get(CV_CAP_PROP_FRAME_HEIGHT);

		//init images
		Mat fr,gr;
		_sum32 = Mat::zeros(Size(wFr,hFr),CV_32FC1);

		//compute initial background
		int iFr=0;
		while(pReader->getClipPtr()->read(fr)&&iFr++<_cQSize) {
			cvtColor(fr,gr,CV_BGR2GRAY); // convert color image to grayscale image
			gr.convertTo(_gr32,CV_32FC1); // 8bit -> 32bit
			_QFrGr32.push_back(_gr32.clone()); // put to queue
			_sum32+=_gr32;
		} _avg32 = _sum32 / _cQSize; // average
		_avg32.convertTo(*this,CV_8UC1); // back to 8bit grayscale
		return true;
	}

	//save background
	bool exportConfig(MDataManager* pData) {
		string imgName = toolpath::joinPath(pData->data_rootPath.c_str(), "background.png");
		if (!toolpath::isFileExist(imgName))
			return imwrite(imgName, *this);
		else
			return true;
		
	}

	//refresh background
	void renew(Mat &fr) {
		//update single/summed frame buffer
		cvtColor(fr, _gr, CV_BGR2GRAY);
		_gr.convertTo(_gr32,CV_32FC1);
		_sum32 = _sum32 - _QFrGr32[0] + _gr32;

		//update background
		_avg32 = _sum32 / _cQSize;
		_avg32.convertTo(*this,CV_8UC1);

		//update buffer queue
		_QFrGr32.pop_front(); // remove 1st one from front end
		_QFrGr32.push_back(_gr32.clone()); // push new one to back end
	}

	Mat getBg() {
		return *this;
	}

	//reload background
	bool reload(const char* szLoadName = "background") {
		string loadName = szLoadName;

		//reload configure data
		ifstream ifs(loadName+".config");
		string loadReaderName;
		ifs>>loadReaderName
			>>_iFrStart
			>>_cQSize;

		//reload buffer queue
		setQSize(_cQSize);
		_QFrGr32.clear();
		initWith(_pClips);

		ifs.close();
		return true;
	}

};


#endif